"""
Mailbox handlers holds state information and references to mail/mailboxes
Written By: Daniel Ortiz
"""
import re
from database.mapclass import Mail, Folder
from twisted.internet import reactor, defer, ssl
from network.pop3 import POP3Factory
from network.imap4 import IMAP4Factory
from twisted.mail import imap4


class MailboxBuffer(object):
    """Mailbox handler keeps a reference to all of the mail in a mailbox"""
    def __init__(self, mailbox, key_cls = Folder, data_cls = Mail):
        self.handler = {}
        self.mailbox = mailbox
        self.key_cls = key_cls
        self.data_cls = data_cls
        self.domain = None
        self.factory = None
    
    def Append(self, folder, mail):
        """Append the mail to the folder"""
        self.AddKey(folder)
        self.handler[folder] = self.handler[folder] + mail
        
    def Add(self, key, object):
        """Add a key or an object to the dictionary"""
        self.AddKey(key)
        self.handler[key].append(object)
    
    def AddKey(self, key):
        """Add a key to the dictionary"""
        if not self.handler.has_key(key):
            self.handler[key] = []
    
    def AddDomain(self, domain):
        """Add the domain to the buffer"""
        self.domain = domain
        
    def AddFactory(self, factory):
        """Add the factory to the buffer"""
        self.factory = factory
    
    def Retrieve(self, key):
        """Get the list of mail that corresponds to folder"""
        if not self.handler.has_key(key):
            return self.handler[key]
    
    def SearchAll(self, value, result):
        """Search the current key for relevant information"""
        reg = re.compile(r'%s' % value)
        for key, value in self.handler.iteritems():
            for m in value:
                try:
                    for t in m.get():
                        s = str(t)
                        if reg.match(s):
                            result.append(m)
                except TypeError:
                    continue    
                
    def HasMail(self, mail):
        """Checks if the buffer has a piece of mail"""
        for key, value in self.handler.iteritems():
            if mail in value:
                return True
        return False
    
    def DeleteMail(self, mail):
        """Delete the mail from the buffer"""
        for key, value in self.handler.iteritems():
            for key, m in enumerate(value):
                if m == mail:
                    value.remove(m)
                    return key
        return None
    
    def HasUID(self, uid, folder):
        """Check in all folders for the relevant uids"""
        f = self.handler.get(folder)
        if f:
            for m in f:
                if int(m.uid) == int(uid):
                    return True
        return False
    
    def ExtractUID(self, mUIDS, folder):
        """Create a message set to remoe the relevant uids"""
        cand = []
        print mUIDS
        for uid in mUIDS:
            if not self.HasUID(uid, folder):
                cand.append(uid)
        print 'Candidate', cand
        if cand:
            s = imap4.MessageSet()
            for c in cand:
                s.add(int(c))
        else:
            s = None
        return s
    
class MailboxHandler(object):
    """Handler keeps a complete state of the mailbox"""
    def __init__(self):
        self.local_handler = []
        self.domain_count = 0
        self.mail_count = 0
        self.folder_count = 0
        self.visual = False
        self.current_mailbox = None
        
    def Append(self, name, message):
        """Append the messages to the mailbox with a certain name"""
        for m in self.local_handler:
            if m.mailbox.alias == name:
                for v in message:
                    self._AddMail(v, m.mailbox)
                
    def AddMailboxes(self, mailboxes):
        """Create buffers representing the mailboxes"""
        for m in mailboxes:
            buf = MailboxBuffer(m)
            self.local_handler.append(buf)
        if mailboxes:
            self.vis.AddMailbox(mailboxes)
    
    def AddDomain(self, d):
        """Bind the domains to the mailboxes"""
        m = self.local_handler[self.domain_count]
        if isinstance(d, list):
            for c in d:
                m.AddDomain(c)
        else:
            m.AddDomain(d)
        self.domain_count = self.domain_count + 1
        
    def AddMail(self, mail, it = True, folder = None):
        """Bind the mail to the mailboxes"""
        buf = self.local_handler[self.mail_count]
        if folder:
            buf.Append(folder, mail)
        else:
            for m in mail:   
                self._AddMail(m, buf)
        if it:
            self.mail_count = self.mail_count + 1
        
    def _AddMail(self, m, buf):
        """Adds the mail"""
        if ':' in m.state:
            folder = m.state.split(':')[1]
            buf.Add(folder, m)
        else:
            buf.Add('.', m)
    
    def VisualizeMail(self, empty_folders = None, update = False):
        """Add all of the folders with mail to the visualization"""
        if not self.visual or update:
            #self.vis.AddMailbox([m.mailbox for m in self.local_handler])
            for m in self.local_handler:
                self.vis.AddMail(m.mailbox, m.handler)
                if m == self.current_mailbox:
                    self.vis.ExpandMailbox(m.mailbox)
                    self.vis.HighlightMailbox(m.mailbox)
            self.visual = True
    
    def AddEmptyFolders(self, folder):
        """Add empty folders to the relevant mailbox"""
        try:
            m = self.local_handler[self.folder_count]
        except IndexError:
            return
        if isinstance(folder, list):
            for f in folder:
                if not f in m.handler.keys():
                    m.AddKey(f.alias)
        else:
            if not folder in m.handler.keys():
                m.AddKey(folder.alias)
        self.folder_count = self.folder_count + 1
    
    def AddFactory(self, f, mailbox):
        """Add the factory the Mailbox buffer"""
        for m in self.local_handler:
            if m.mailbox == mailbox:
                m.AddFactory(f)
        
    def Search(self, value):
        """Search the array of buffers for the value"""
        real = r'%s' % value
        result = []
        self.vis.NewSearch(real)
        for b in self.local_handler:
            b.SearchAll(value, result)
        self.vis.AppendSearchResults(real, result)
        
    def BindVisualization(self, vis):
        """Bind the class to the visualization of the mailbox"""
        self.vis = vis
        
    def BindNotebook(self, notebook):
        """Bind the notebook to the handler"""
        self.notebook = notebook
        
    def BindDatabase(self, database):
        """Bind the mailbox handler to the database"""
        self.database = database
        
    def GetDomainMailbox(self, d):
        """Returns the mailbox of a domain"""
        for f in self.local_handler:
            if f.domain == d:
                return f.mailbox
        return None
    
    def GetDomainFactory(self, d):
        """Return the factory corresponding to a domain"""
        for f in self.local_handler:
            if f.domain == d:
                return f.factory
        return None

    def GetMailboxDomain(self, m):
        """Return the domain of the mailbox"""
        for f in self.local_handler:
            if f.mailbox == m:
                return f.domain
        return None
    
    def GetFactories(self):
        """Retrieve all of the valid factories for the corresponding mailboxes"""
        for m in self.local_handler:
            if m.factory:
                yield m.factory
            
    def SetCurrentMailbox(self, mailbox):
        """Sets the current mailbox"""
        self.current_mailbox = mailbox    
        
    def GetDomains(self):
        """Get the current domains"""
        for m in self.local_handler:
            yield m.domain
    
    def ShowMail(self, mail):
        """Load a new page visualizing the mail"""
        self.notebook.NewMail(mail)
        
    def GetCurrentDomain(self):
        """Iterate over the mailboxes searching for the current domain"""
        for m in self.local_handler:
            if m.mailbox == self.current_mailbox:
                return m.domain
        return self.local_handler[0].domain
    
    def BeginSend(self, mail):
        """Initial creates a temporary folder containing the send folder"""
        self.vis.AddSend(mail, temporary = True)
        
    def EndSend(self, mail, success):
        """Finalize the sent data"""
        self.vis.AddSend(mail, temporary = False,  success = success)
        if success:
            self.AddToSend(mail)
            
    def AddToSend(self, mail):
        """Save the mail in the sent meta folder, and search in the current mailbox for
        a potential folder addition"""
        print mail
        
    def Delete(self, mail):
        """Delete the mail from the relevant folder"""
        self.notebook.DeletePanel(mail)
        for m in self.local_handler:
            if m.HasMail(mail):
                m.mailbox.size = m.mailbox.size - 1
                key = self.database.DeleteMail(m.mailbox, mail)
                key = m.DeleteMail(mail)
                factory = m.factory
        if isinstance(factory, POP3Factory):
            factory.DeleteMail(key, self.HandleDeleteResults)
        elif isinstance(factory, IMAP4Factory):
            print mail.flags
    
    def HandleDeleteResults(self, data):
        """Handle the delete results from the mailbox"""
        print 'Results', data
        
    def HasUID(self, nUID, mailbox):
        """Returns true if the mailbox has a certain uid"""
        for m in self.local_handler:
            if m.mailbox == mailbox:
                return m.HasUID(nUID)
        return False
    
    def PurgeUID(self, mUIDs, mailbox, folder):
        """Create a message set for the inputted UIDS, comparing them to the resident mailbox"""
        for m in self.local_handler:
            if m.mailbox == mailbox:
                s = m.ExtractUID(mUIDs, folder)
                print s
                print mailbox, folder
                return s
        return None
        
    